package com.zzu.base.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzu.base.common.enums.ResultStatusEnum;
import com.zzu.base.common.enums.UserStatusEnum;
import com.zzu.base.common.pojo.LoginUser;
import com.zzu.base.common.utils.JwtUtil;
import com.zzu.base.controller.user.vo.UserPageReqVO;
import com.zzu.base.entity.RoleDO;
import com.zzu.base.entity.UserDO;
import com.zzu.base.entity.UserRoleDO;
import com.zzu.base.exception.ProcessException;
import com.zzu.base.mapper.RoleMapper;
import com.zzu.base.mapper.UserMapper;
import com.zzu.base.mapper.UserRoleMapper;
import com.zzu.base.service.UserService;
import com.zzu.base.controller.auth.vo.LoginReqVO;
import com.zzu.base.controller.auth.vo.RegisterReqVO;
import io.jsonwebtoken.Claims;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, UserDO> implements UserService {
    @Resource
    private UserMapper userMapper;
    @Resource
    private AuthenticationManager authenticationManager;
    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private RoleMapper roleMapper;
    @Resource
    private UserRoleMapper userRoleMapper;
    @Resource
    private RedisTemplate<String,Object> redisTemplate;
    @Override
    public List<RoleDO> getRolesByUserId(Long userId) {
        QueryWrapper<UserRoleDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId);
        List<UserRoleDO>userRoleDOS= userRoleMapper.selectList(queryWrapper);
        if (userRoleDOS==null ){
            throw new ProcessException(ResultStatusEnum.BAD_REQUEST,"参数错误");
        }
        List<RoleDO>roleDOS=new ArrayList<RoleDO>();
        for(UserRoleDO userRoleDO:userRoleDOS){
            roleDOS.add(roleMapper.selectById(userRoleDO.getRoleId()));
        }
        return roleDOS;
    }

    @Override
    public Boolean addUserRole(Long userId, Long roleId) {
        UserRoleDO userRoleDO = new UserRoleDO();
        UserDO userDO = userMapper.selectById(userId);
        RoleDO roleDO = roleMapper.selectById(roleId);
        if (userDO==null || roleDO==null){
            throw new ProcessException(ResultStatusEnum.BAD_REQUEST,"参数错误");
        }
        userRoleDO.setUserId(userId);
        userRoleDO.setRoleId(roleId);
        return userRoleMapper.insert(userRoleDO)>0;
    }

    @Override
    public Boolean removeUserRole(Long userId, Long roleId) {
        QueryWrapper<UserRoleDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId);
        queryWrapper.eq("role_id", roleId);
        return userRoleMapper.delete(queryWrapper)>0;
    }

    @Override
    @Transactional
    public Boolean removeUser(Long id) {
        QueryWrapper<UserRoleDO>queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", id);
        userRoleMapper.delete(queryWrapper);
        return userMapper.deleteById(id)>0;
    }

    @Override
    public Boolean update(UserDO userDO) {
        QueryWrapper<UserDO>queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_name", userDO.getUserName());
        UserDO oldUser = userMapper.selectOne(queryWrapper);
        if(oldUser!=null){
            throw new  ProcessException(ResultStatusEnum.BAD_REQUEST,"已有重复用户名");
        }
        return userMapper.updateById(userDO)>0;
    }

    @Override
    public IPage<UserDO> getUserPage(UserPageReqVO userPageReqVO) {
        Page<UserDO> page = new Page<>();
        page.setCurrent(userPageReqVO.getPageNum());
        page.setSize(userPageReqVO.getPageSize());
        QueryWrapper<UserDO> queryWrapper = new QueryWrapper<>();
        if (userPageReqVO.getUsername() != null) {
            queryWrapper.like("user_name", userPageReqVO.getUsername());
        }
        return userMapper.selectPage(page,queryWrapper);
    }

    @Override
    public Boolean logout(HttpServletRequest request,
                          HttpServletResponse response) {
        String token = request.getHeader("token");

        // 记录请求日志
        log.debug("处理请求: {} {}", request.getMethod(), request.getRequestURI());
        Claims claims = JwtUtil.parseJWT(token);
        LocalDateTime expireTime= JwtUtil.getExpireTime(claims);
        LocalDateTime now = LocalDateTime.now();
        Duration duration=Duration.between(now,expireTime);
        long remainingMillis = duration.toMillis();
        if (remainingMillis <= 0) {
            // Token已过期，无需加入黑名单
            return false;
        }
        redisTemplate.opsForValue().set("not_used" + token, "invalid", remainingMillis, TimeUnit.MILLISECONDS);
        return true;
    }

    @Override
    public UserDO getUserByUsername(String username) {
        QueryWrapper<UserDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_name", username);
        return userMapper.selectOne(queryWrapper);
    }

    @Override
    public String login(LoginReqVO loginReqVO) {
        try {
            // 创建认证令牌
            UsernamePasswordAuthenticationToken authenticationToken =
                    new UsernamePasswordAuthenticationToken(
                            loginReqVO.getUserName(),
                            loginReqVO.getPassword()
                    );

            // 执行认证
            Authentication authentication = authenticationManager.authenticate(authenticationToken);

            // 认证成功，获取用户信息
            LoginUser loginUser = (LoginUser) authentication.getPrincipal();

            // 生成JWT令牌
            String token = JwtUtil.createJWT(
                    loginUser.getUsername(),
                    loginUser.getRoles(),
                    loginUser.getPermissions()
            );

            // 清除敏感信息
            authenticationToken.eraseCredentials();

            return token;

        } catch (BadCredentialsException e) {
            // 密码错误
            log.warn("登录失败: 用户名或密码错误 - {}",loginReqVO.getUserName());
            throw new ProcessException(ResultStatusEnum.BAD_REQUEST, "用户名或密码错误");
        } catch (DisabledException e) {
            // 用户禁用
            log.warn("登录失败: 用户已禁用 - {}", loginReqVO.getUserName());
            throw new ProcessException(ResultStatusEnum.FORBIDDEN, "用户已禁用");
        } catch (LockedException e) {
            // 用户锁定
            log.warn("登录失败: 用户已锁定 - {}", loginReqVO.getUserName());
            throw new ProcessException(ResultStatusEnum.FORBIDDEN, "用户已锁定");
        } catch (AuthenticationException e) {
            // 其他认证异常
            log.warn("登录失败: 认证异常 - {}", loginReqVO.getUserName(), e);
            throw new ProcessException(ResultStatusEnum.BAD_REQUEST, "认证失败");
        } catch (Exception e) {
            // 系统异常
            log.error("登录过程发生异常 - {}", loginReqVO.getUserName(), e);
            throw new ProcessException(ResultStatusEnum.INTERNAL_SERVER_ERROR, "系统异常，请稍后重试");
        }
    }

    @Override
    public int register(RegisterReqVO registerReqVO) {
        UserDO userDO = new UserDO();
        userDO.setEmail(registerReqVO.getEmail());
        userDO.setUserName(registerReqVO.getUserName());
        userDO.setPassword(passwordEncoder.encode( registerReqVO.getPassword()));
        userDO.setSex(registerReqVO.getSex());
        userDO.setStatus(UserStatusEnum.ON);
        userDO.setNickName(registerReqVO.getNickName());
        userDO.setPhoneNumber(registerReqVO.getPhoneNumber());
        return userMapper.insert(userDO);
    }
}
